#include "manager_crc.h"
#include <rtthread.h>

static unsigned char crc8_lut[256];   /**< crc8 LUT */
static unsigned short crc16_lut[256]; /**< crc16 LUT */
static unsigned int crc32_lut[256];   /**< crc32 LUT */

unsigned char reverse8(unsigned char data)
{
    unsigned char i;
    unsigned char temp = 0;
    for (i = 0; i < 8; i++)
        temp |= ((data >> i) & 0x01) << (7 - i);
    return temp;
}

unsigned short reverse16(unsigned short data)
{
    unsigned char i;
    unsigned short temp = 0;
    for (i = 0; i < 16; i++)
        temp |= ((data >> i) & 0x0001) << (15 - i);
    return temp;
}

void crc8_init_lut(const unsigned char poly, unsigned char *lut)
{
    unsigned int i, j;

    if (lut == RT_NULL)
        return;

    for (j = 0; j < 256; j++)
    {
        unsigned char crc = j;

        for (i = 0; i < 8; i++)
            if (crc & 0x80)
                crc = (crc << 1) ^ poly;
            else
                crc <<= 1;

        lut[j] = crc;
    }
}

void crc16_init_lut(const unsigned short poly, unsigned short *lut)
{
    unsigned int i;

    if (lut == RT_NULL)
        return;

    for (i = 0; i < 256; i++)
    {
        unsigned short crc = 0;
        unsigned int j;

        crc = (unsigned short)(i << 8);

        for (j = 0; j < 8; j++)
        {
            if (crc & 0x8000)
                crc = (crc << 1) ^ poly;
            else
                crc <<= 1;
        }

        lut[i] = crc;
    }
}

void crc32_init_lut(const unsigned int poly, unsigned int *lut)
{
    unsigned int i, j;

    if (lut == RT_NULL)
        return;

    for (i = 0; i < 256; i++)
    {
        unsigned int crc = (i << 24);

        for (j = 0; j < 8; j++)
            if (crc & 0x80000000L)
                crc = (crc << 1) ^ poly;
            else
                crc <<= 1;

        lut[i] = crc;
    }
}

unsigned char crc8_calc_lut(const unsigned char *data,
                            unsigned int data_len,
                            unsigned char crc,
                            const unsigned char *lut)
{
    if (data == RT_NULL || lut == RT_NULL)
        return crc;

    while (data_len--)
        crc = lut[*data++ ^ crc];

    return crc;
}

unsigned short crc16_calc_lut(const unsigned char *data,
                              unsigned int data_len,
                              unsigned short crc,
                              const unsigned short *lut)
{
    if (data == RT_NULL || lut == RT_NULL)
        return crc;

    while (data_len--)
        crc = lut[(crc >> 8) ^ *data++] ^ (crc << 8);

    return crc;
}

unsigned int crc32_calc_lut(const unsigned char *data,
                            unsigned short data_len,
                            unsigned int crc,
                            const unsigned int *lut)
{
    if (data == RT_NULL || lut == RT_NULL)
        return crc;

    while (data_len--)
        crc = lut[(crc >> 24) ^ *data++] ^ (crc << 8);

    return crc;
}

unsigned char crc8_calc(const unsigned char *data, unsigned int data_len)
{
    return crc8_calc_lut(data, data_len, CRC8_INIT_VALUE, crc8_lut);
}

unsigned short crc16_calc(const unsigned char *data, unsigned int data_len)
{
    return crc16_calc_lut(data, data_len, CRC16_INIT_VALUE, crc16_lut);
}

unsigned int crc32_calc(const unsigned char *data, unsigned int data_len)
{
    return crc32_calc_lut(data, data_len, CRC32_INIT_VALUE, crc32_lut);
}

void crc8_init(void)
{
    crc8_init_lut(CRC8_POLYNOMIAL, crc8_lut);
}

void crc16_init(void)
{
    crc16_init_lut(CRC16_POLYNOMIAL, crc16_lut);
}

void crc32_init(void)
{
    crc32_init_lut(CRC32_POLYNOMIAL, crc32_lut);
}